package io.github.wslxm.springbootplus2.manage.sys.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.github.wslxm.springbootplus2.constant.CacheKey;
import io.github.wslxm.springbootplus2.constant.RoleConst;
import io.github.wslxm.springbootplus2.core.base.model.BasePage;
import io.github.wslxm.springbootplus2.core.base.service.impl.BaseServiceImpl;
import io.github.wslxm.springbootplus2.core.config.error.ErrorException;
import io.github.wslxm.springbootplus2.core.result.ResultType;
import io.github.wslxm.springbootplus2.core.utils.XjBeanUtil;
import io.github.wslxm.springbootplus2.core.utils.threadpool.XjThreadUtil;
import io.github.wslxm.springbootplus2.core.utils.validated.XjValidUtil;
import io.github.wslxm.springbootplus2.manage.sys.cache.CacheService;
import io.github.wslxm.springbootplus2.manage.sys.dao.*;
import io.github.wslxm.springbootplus2.manage.sys.mapper.SysRoleMapper;
import io.github.wslxm.springbootplus2.manage.sys.model.dto.SysRoleDTO;
import io.github.wslxm.springbootplus2.manage.sys.model.entity.*;
import io.github.wslxm.springbootplus2.manage.sys.model.query.SysRoleQuery;
import io.github.wslxm.springbootplus2.manage.sys.model.vo.SysRoleVO;
import io.github.wslxm.springbootplus2.manage.sys.model.vo.next.SysRoleAuthVo;
import io.github.wslxm.springbootplus2.manage.sys.model.vo.next.SysRoleMenuVo;
import io.github.wslxm.springbootplus2.manage.sys.model.vo.next.SysUserRolesVO;
import io.github.wslxm.springbootplus2.manage.sys.service.SysRoleService;
import io.github.wslxm.springbootplus2.starter.redis.lock.XjDistributedLock;
import io.github.wslxm.springbootplus2.starter.redis.util.RedisUtil;
import io.github.wslxm.springbootplus2.utils.JwtUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;

/**
 * 角色管理
 *
 * @author wangsong
 */
@Service
public class SysRoleServiceImpl extends BaseServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {

    @Autowired
    private SysRoleUserDao sysRoleUserDao;
    @Autowired
    private SysRoleMenuDao sysRoleMenuDao;
    @Autowired
    private SysRoleAuthDao sysRoleAuthDao;
    @Autowired
    private SysAuthorityDao sysAuthorityDao;
    @Autowired
    private SysRoleDao sysRoleDao;
    @Autowired
    private SysMenuDao sysMenuDao;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private CacheService cacheService;

    @Override
    public BasePage<SysRoleVO> findPage(SysRoleQuery query) {
        Page<SysRoleVO> page = new Page<>(query.getCurrent(), query.getSize());
        page = page.setRecords(baseMapper.list(page, query));
        return XjBeanUtil.pageVo(page);
    }

    /**
     * 添加角色-默认有所有URL 权限
     *
     * @param dto
     * @return java.lang.Boolean
     * @author wangsong
     * @date 2020/9/19 0019 10:56
     * @version 1.0.1
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @XjDistributedLock(lockName = "'xj-sys-role_'+#dto.code", waitTime = 5L)
    public String insert(SysRoleDTO dto) {
        this.isCodeRepeat(dto.getCode(), null);
        SysRole role = dto.convert(SysRole.class);
        this.save(role);
        XjThreadUtil.asyncExecute(()->{
            // 给角色分配菜单权限(先删除后添加)
            if (dto.getMenuIds() != null) {
                sysRoleMenuDao.updRoleMenus(role.getId(), dto.getMenuIds());
            }
            // 给角色分配菜单权限(先删除后添加)
            if (dto.getAuthIds() != null) {
                sysRoleAuthDao.updRoleAuths(role.getId(), dto.getAuthIds());
            }
        });
        return role.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @XjDistributedLock(lockName = "'xj-sys-role_'+#dto.code", waitTime = 5L)
    public Boolean upd(String id, SysRoleDTO dto) {
        this.isCodeRepeat(dto.getCode(), id);
        // 超管角色只有超管账号有权限编辑
        SysRole sysRole = this.getById(id);
        if (sysRole.getCode().equals(RoleConst.ROLE_SYS)) {
            XjValidUtil.isTrue(dto.getDisable().equals(1), "sys.role.disable.superManage");
            XjValidUtil.isTrue(!sysRole.getCode().equals(dto.getCode()), "sys.role.edit.superManageCode");
            boolean isSuperManage = this.isSuperManageByUserId(JwtUtil.getJwtUser(request).getUserId());
            XjValidUtil.isTrue(!isSuperManage, "sys.role.edit.superManage");
        }
        SysRole role = dto.convert(SysRole.class);
        role.setId(id);
        boolean b = this.updateById(role);
        XjThreadUtil.asyncExecute(()->{
            // 给角色分配菜单权限(先删除后添加)
            if (dto.getMenuIds() != null) {
                sysRoleMenuDao.updRoleMenus(role.getId(), dto.getMenuIds());
            }
            // 给角色分配菜单权限(先删除后添加)
            if(dto.getAuthIds()!=null){
                sysRoleAuthDao.updRoleAuths(role.getId(), dto.getAuthIds());
            }
            // 清除此角色的用户接口权限缓存
            this.delAuthCacheByRoleCode(sysRole.getCode());
            // code 发生变化更新用户的角色数据
            if (StringUtils.isNotBlank(dto.getCode()) ) {
                if(!dto.getCode().equals(sysRole.getCode())){
                    cacheService.updateUserTokenValueCacheByRoleId(id);
                }
            }
        });
        return b;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean del(String roleId) {
        // 禁止删除超管角色
        SysRole sysRole = this.getById(roleId);
        XjValidUtil.isTrue(sysRole.getCode().equals(RoleConst.ROLE_SYS), "sys.role.delete.superManage");

        // 删除角色和角色相关的关系表
        sysRoleUserDao.delByRoleId(roleId);
        sysRoleMenuDao.delByRoleId(roleId);
        // 清除拥有此角色的用户接口权限缓存
        this.delAuthCacheByRoleCode(sysRole.getCode());
        return this.removeById(roleId);
    }

    @Override
    public List<SysRoleMenuVo> findMenuIdsById(String roleId) {
        List<SysRoleMenu> roleMenu = sysRoleMenuDao.findMenuIdsByRoleId(roleId);
        if (roleMenu.isEmpty()) {
            return CollUtil.newArrayList();
        }
        List<String> roleMenuIds = roleMenu.stream().map(SysRoleMenu::getMenuId).toList();
        List<SysMenu> list = sysMenuDao.list(new LambdaQueryWrapper<SysMenu>()
                .select(SysMenu::getId, SysMenu::getRoot)
                .in(SysMenu::getId, roleMenuIds));
        return XjBeanUtil.listVo(list, SysRoleMenuVo.class);
    }

    @Override
    public List<SysRoleAuthVo> findAuthIdsById(String roleId) {
        List<SysRoleAuth> roleAuth = sysRoleAuthDao.findAuthByRoleId(roleId);
        if (roleAuth.isEmpty()) {
            return CollUtil.newArrayList();
        }
        List<String> roleAuthIds = roleAuth.stream().map(SysRoleAuth::getAuthId).toList();
        List<SysAuthority> list = sysAuthorityDao.list(new LambdaQueryWrapper<SysAuthority>()
                .select(SysAuthority::getId, SysAuthority::getPid)
                .in(SysAuthority::getId, roleAuthIds));
        return XjBeanUtil.listVo(list, SysRoleAuthVo.class);
    }


    @Override
    public boolean isSuperManageByUserId(String userId) {
        return sysRoleDao.isSuperManageByUserId(userId);
    }

    @Override
    public List<String> findRoleCodesByUserId(String userId) {
        return sysRoleDao.findRoleCodesByUserId(userId);
    }

    @Override
    public List<SysUserRolesVO> findRolesByUserId(String userId) {
        return sysRoleDao.findRolesByUserId(userId);
    }

    @Override
    public Map<String, List<SysUserRolesVO>> findRolesByUserIds(List<String> userIds) {
        return sysRoleDao.findRolesByUserIds(userIds);
    }

    @Override
    public String findRoleIdByCode(String code) {
        return sysRoleDao.findRoleIdByCode(code);
    }

    @Override
    public Map<String, String> findRoleIdsByCodes(List<String> codes) {
        return sysRoleDao.findRoleIdsByCodes(codes);
    }


    @Override
    public boolean isSuperManageByRoleIds(List<String> roleIds) {
        return sysRoleDao.isSuperManageByRoleIds(roleIds);
    }

    @Override
    public SysRole findSuperManage() {
        return sysRoleDao.findSuperManage();
    }


    /**
     * 角色code重复验证
     *
     * @author wangsong
     * @mail 1720696548@qq.com
     * @date 2022/8/20 0020 14:33
     * @version 1.0.0
     */
    private void isCodeRepeat(String code, String excludeId) {
        long count = this.count(new LambdaQueryWrapper<SysRole>()
                .eq(SysRole::getCode, code)
                .ne(excludeId != null, SysRole::getId, excludeId)
        );
        if (count > 0) {
            throw new ErrorException(ResultType.BASE_CODE_REPEAT);
        }
    }


    /**
     * 删除当前角色的权限缓存
     *
     * @return
     */
    public boolean delAuthCacheByRoleCode(String roleCode) {
        String prefix = redisUtil.getPrefix(false);
        String redisCacheKey = prefix + CacheKey.GATEWAY_ROLE_AUTH_MAPS + "::" + roleCode;
        redisUtil.delete(redisCacheKey);
        return true;
    }
}